﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.Regions.Behaviors;
using Microsoft.Practices.ServiceLocation;

namespace UnityExtensions
{
    /// <summary>
    /// Populates the target region with the views registered to it in the <see cref="T:Microsoft.Practices.Prism.Regions.IRegionViewRegistry" />.
    /// </summary>
    /// <remarks>
    /// This class behaves in a similar way to the base <see cref="AutoPopulateRegionBehavior"/>, however uses the alternative <see cref="IScopedRegionViewRegistry"/>
    /// instance instead, passing in the container located by searching through the logical tree.
    /// </remarks>
    public class ScopedAutoPopulateRegionBehavior : AutoPopulateRegionBehavior, IHostAwareRegionBehavior
    {
        private readonly IScopedRegionViewRegistry _scopedRegionViewRegistry;
        private readonly IServiceLocator _defaultServiceLocator;

        /// <summary>
        /// Initialises a new instance of <see cref="ScopedAutoPopulateRegionBehavior"/>.
        /// </summary>
        /// <param name="scopedRegionViewRegistry">View registry to use.</param>
        /// <param name="defaultServiceLocator">The default service locator to use when no locally scoped service locator can be found.</param>
        public ScopedAutoPopulateRegionBehavior(IScopedRegionViewRegistry scopedRegionViewRegistry, IServiceLocator defaultServiceLocator)
            : base(null)
        {
            if (scopedRegionViewRegistry == null)
            {
                throw new ArgumentNullException("scopedRegionViewRegistry");
            }
            if (defaultServiceLocator == null)
            {
                throw new ArgumentNullException("defaultServiceLocator");
            }
            _scopedRegionViewRegistry = scopedRegionViewRegistry;
            _defaultServiceLocator = defaultServiceLocator;
        }

        public DependencyObject HostControl { get; set; }

        /// <summary>
        /// Gets the <see cref="IServiceLocator"/> to use when resolving child view.s
        /// </summary>
        protected virtual IServiceLocator ServiceLocator
        {
            get
            {
                return FindServiceLocator(HostControl) ?? _defaultServiceLocator;
            }
        }

        protected override void OnAttach()
        {
            if (string.IsNullOrEmpty(Region.Name))
            {
                Region.PropertyChanged += Region_PropertyChanged;
                return;
            }
            StartPopulatingContent();
        }

        protected override IEnumerable<object> CreateViewsToAutoPopulate()
        {
            return _scopedRegionViewRegistry.GetContents(Region.Name).Select(v => v(Region, ServiceLocator));
        }

        private void StartPopulatingContent()
        {
            foreach (var view in CreateViewsToAutoPopulate())
            {
                AddViewIntoRegion(view);
            }
            _scopedRegionViewRegistry.ContentRegistered += OnContentRegistered;
        }

        private void OnContentRegistered(object sender, ViewRegisteredEventArgs args)
        {
            if (args.RegionName == Region.Name)
            {
                AddViewIntoRegion(args.ResolveView(Region, ServiceLocator));
            }
        }

        private void Region_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name" && !string.IsNullOrEmpty(Region.Name))
            {
                Region.PropertyChanged -= Region_PropertyChanged;
                StartPopulatingContent();
            }
        }

        private IServiceLocator FindServiceLocator(DependencyObject dependencyObject)
        {
            var container = RegionManager.GetRegionContext(dependencyObject) as IServiceLocator;
            if (container != null)
            {
                return container;
            }
            DependencyObject parent = LogicalTreeHelper.GetParent(dependencyObject);
            if (parent != null)
            {
                return FindServiceLocator(parent);
            }
            return null;
        }
    }
}
